import base64
import json
import random
import time
from urllib.parse import urlencode, unquote

from curl_cffi import requests

from app.chatgpt.arkose.bda import generate_bda_info
from app.chatgpt.arkose.capsolver import Capsolver
from app.chatgpt.arkose.dcrypto import BDACrypto
from app.utils.decorator import cache_func_data
from app.chatgpt.arkose.utils import get_bio

from io import BytesIO
from PIL import Image


class Arkose:
    capsolver = Capsolver()

    def __init__(self, user_agent):
        self.user_agent = user_agent
        self.session = requests.Session()
        self.public_key = "0A1D34FC-659D-4E23-B17B-694DCFCF6A6C"
        self.server_url = "https://tcr9i.openai.com"

        self.session.headers.update({
            "content-type": "application/x-www-form-urlencoded",
            "User-Agent": user_agent,
            "accept-language": "zh-CN,zh;q=0.9"
        })
        self.session.impersonate = 'safari'
        self.session.timeout = 10

    def __repr__(self):
        return self.__class__.__name__

    def _get_arkose_token(self, blob_value):
        url = f"{self.server_url}/fc/gt2/public_key/0A1D34FC-659D-4E23-B17B-694DCFCF6A6C"
        bda_info = generate_bda_info(self.user_agent)
        payload = {
            "bda": bda_info['bda'],
            "public_key": self.public_key,
            "site": "https://auth0.openai.com",
            "userbrowser": self.user_agent,
            "capi_version": "2.9.0",
            "capi_mode": "lightbox",
            "style_theme": "default",
            "rnd": random.uniform(0, 1),
            "data[blob]": blob_value
        }
        self.session.headers.update({'X-Ark-Esync-Value': bda_info['X-Ark-Esync-Value']})
        # print("url", url)
        res = requests.post(url, data=payload)
        self.session.headers.pop('X-Ark-Esync-Value')

        return res.json()

    def get_timestamp(self):
        time_str = str(int(time.time() * 1000))
        value = f"{time_str[:7]}00{time_str[7:]}"
        cookie = f"timestamp={value}"
        return cookie, value

    @staticmethod
    def parse_arkose_token(token):
        token = "session=" + unquote(token)
        assoc = {}
        for field in token.split("|"):
            s = field.partition("=")
            key, value = s[0], s[-1]
            assoc[key] = value
        return assoc

    def get_gfct(self, assoc):
        url = f"{self.server_url}/fc/gc/?token={assoc['session']}"
        self.session.headers.update({
            "Accept": "*/*",
            "Referer": f"{self.server_url}/v2/2.9.0/enforcement.b3b1c9343f2ef3887d61d74272d6a3af.html",
            "Sec-Fetch-Dest": "empty",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Site": "same-origin",
        })
        self.session.get(url)

        surl = f"{self.server_url}/fc/assets/ec-game-core/game-core/1.23.2/standard/index.html"
        self.session.headers.update({
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
            "Referer": f"{self.server_url}/v2/2.9.0/enforcement.b3b1c9343f2ef3887d61d74272d6a3af.html",
            "Sec-Fetch-Dest": "iframe",
            "Sec-Fetch-Mode": "navigate",
            "Sec-Fetch-Site": "same-origin",
            "upgrade-insecure-requests": "1",
        })
        res = self.session.get(surl, params=assoc)

        # /fc/a/
        tCookie, tValue = self.get_timestamp()
        self.session.cookies.set('timestamp', tValue, domain=self.server_url.replace("https://", ""))
        self.session.headers.update({
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "Origin": self.server_url,
            "X-Newrelic-Timestamp": tValue,
            "X-Requested-With": "XMLHttpRequest",
        })
        data1 = {
            "sid": assoc["r"],
            "session_token": assoc["session"],
            "analytics_tier": assoc["at"],
            "disableCookies": False,
            "render_type": "canvas",
            "is_compatibility_mode": False,
            "category": "Site URL",
            "action": "https://tcr9i.openai.com/v2/2.9.0/enforcement.b3b1c9343f2ef3887d61d74272d6a3af.html"
        }
        data1 = urlencode(data1)
        response = self.session.post(f"{self.server_url}/fc/a/", data=data1)

        tCookie, tValue = self.get_timestamp()
        self.session.cookies.set('timestamp', tValue, domain=self.server_url.replace("https://", ""))

        self.session.headers.update({
            "Accept": "*/*",
            "X-Newrelic-Timestamp": tValue,
            "X-Requested-With": "XMLHttpRequest",
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "Origin": self.server_url,
            "Referer": surl,
            "Sec-Fetch-Dest": "empty",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Site": "same-origin",
        })

        data = {
            "token": assoc["session"],
            "sid": assoc["r"],
            "render_type": "canvas",
            "lang": "",
            "isAudioGame": False,
            "analytics_tier": assoc["at"],
            "is_compatibility_mode": False,
            "apiBreakerVersion": "green"
        }

        response = self.session.post(f"{self.server_url}/fc/gfct/", data=data)
        # print(assoc)
        # print(response.json())
        gfct_json = response.json()

        tCookie, tValue = self.get_timestamp()
        self.session.cookies.set('timestamp', tValue, domain=self.server_url.replace("https://", ""))
        self.session.headers.update({
            "X-Newrelic-Timestamp": tValue,
            "X-Requested-With": "XMLHttpRequest",
        })
        data2 = {
            "sid": assoc["r"],
            "session_token": assoc["session"],
            "analytics_tier": assoc["at"],
            "disableCookies": False,
            "game_token": gfct_json["challengeID"],
            "game_type": gfct_json["game_data"]["gameType"],
            "render_type": "canvas",
            "is_compatibility_mode": False,
            "category": "loaded",
            "action": "game loaded"
        }
        data2 = urlencode(data2)
        response = self.session.post(f"{self.server_url}/fc/a/", data=data2)
        return gfct_json

    def get_arkose_token(self, blob_value):
        arkose_json = self._get_arkose_token(blob_value)
        arkose_token = arkose_json["token"]
        assoc = self.parse_arkose_token(arkose_token)

        if "sup=1" in arkose_token:
            url = f"{self.server_url}/fc/a/"
            params = {
                "callback": f"__jsonp_{int(time.time() * 1000)}",
                "category": "loaded",
                "action": "game loaded",
                "session_token": assoc["session"],
                "data[public_key]": self.public_key,
                "data[site]": "https://auth0.openai.com"
            }
            res = self.session.get(url, params=params)
            print(res.text)
            print("Arkose token is valid")
            return arkose_json
        else:
            print("Arkose token is not valid")
            # return arkose_json

            gfct_json = self.get_gfct(assoc)
            self.start_game(assoc, gfct_json)
            return arkose_json

    @cache_func_data(cache_time=86400)
    def get_imgcontent(self, url):
        response = self.session.get(url)
        return response.content

    @cache_func_data(cache_time=86400)
    def funcaptcha_solver(self, game_variant, url):
        content = self.get_imgcontent(url)
        base64_data = base64.b64encode(content).decode("utf-8")
        result = self.capsolver.create_task(game_variant, base64_data)
        return result

    def start_game(self, assoc, gfct_json):

        tCookie, tValue = self.get_timestamp()
        crypto = BDACrypto(f"REQUESTED{assoc['session']}ID")
        fingerprint_str = json.dumps({"sc": [190, 253]}, separators=(',', ':'))
        requested_id = json.dumps(crypto.re_encrypt_v2(fingerprint_str), separators=(',', ':'), sort_keys=True)

        self.session.cookies.set('timestamp', tValue, domain=self.server_url.replace("https://", ""))
        self.session.headers.update({
            "X-Newrelic-Timestamp": tValue,
            "X-Requested-ID": requested_id,
            "X-Requested-With": "XMLHttpRequest",
        })
        data3 = {
            "sid": assoc["r"],
            "session_token": assoc["session"],
            "analytics_tier": assoc["at"],
            "disableCookies": False,
            "game_token": gfct_json["challengeID"],
            "game_type": gfct_json["game_data"]["gameType"],
            "render_type": "canvas",
            "is_compatibility_mode": False,
            "category": "begin app",
            "action": "user clicked verify"
        }
        data3 = urlencode(data3)
        response = self.session.post(f"{self.server_url}/fc/a/", data=data3)
        print(response.json())

        tCookie, tValue = self.get_timestamp()
        self.session.cookies.set('timestamp', tValue, domain=self.server_url.replace("https://", ""))

        game_variant = gfct_json["game_data"]["instruction_string"] if gfct_json["game_data"]["gameType"] == 4 else \
            gfct_json["game_data"]["game_variant"]
        game_variant = game_variant or "3d_rollball_animalss"

        print(game_variant)
        guess = []
        for i in range(gfct_json["game_data"]["waves"]):
            url = gfct_json["game_data"]["customGUI"]["_challenge_imgs"][i]

            content = self.get_imgcontent(url)
            print("content", url)
            image_bytes = BytesIO(content)
            image = Image.open(image_bytes)
            image.show()

        #     res = self.funcaptcha_solver(game_variant, url)
        #     guess.append({"index": res["solution"]["objects"][0]})
        #
        # self.put_answer(gfct_json, guess, assoc)

        # break

    def get_tguess_crypt(self, dapib_url, session_token, guess):
        # https://github.com/unfuncaptcha/tguess
        data = {
            "guess": guess,
            "dapib_url": dapib_url,
            "session_token": session_token,
        }
        response = requests.post("http://127.0.0.1:3000/tguess", json=data)
        res_json = response.json()
        if not res_json["success"]:
            raise Exception(res_json["message"])
        tguess = response.json()["tguess"]

        crypto = BDACrypto(session_token)
        fingerprint_str = json.dumps(json.dumps(tguess))
        tguess_crypt = json.dumps(crypto.re_encrypt_v2(fingerprint_str), separators=(',', ':'), sort_keys=True)

        return tguess_crypt

    def put_answer(self, gfct_json, guess, assoc):

        crypto = BDACrypto(gfct_json["session_token"])
        fingerprint_str = json.dumps(guess)
        guess_crypt = json.dumps(crypto.re_encrypt_v2(fingerprint_str), separators=(',', ':'), sort_keys=True)

        answer_url = f"{self.server_url}/fc/ca/"

        self.session.headers.update({"Content-Type": "application/x-www-form-urlencoded"})
        answer_data = {
            "session_token": gfct_json["session_token"],
            "game_token": gfct_json["challengeID"],
            "sid": assoc["r"],
            "guess": guess_crypt,
            "render_type": "canvas",
            "analytics_tier": assoc["at"],
            "bio": get_bio(),
            "is_compatibility_mode": False
        }
        tguess_crypt = self.get_tguess_crypt(gfct_json["dapib_url"], gfct_json["session_token"], guess)
        answer_data["tguess"] = tguess_crypt

        tCookie, tValue = self.get_timestamp()

        crypto = BDACrypto(f"REQUESTED{gfct_json['session_token']}ID")
        fingerprint_str = json.dumps({"sc": [190, 253]}, separators=(',', ':'))
        requestedId = json.dumps(crypto.re_encrypt_v2(fingerprint_str), separators=(',', ':'), sort_keys=True)

        self.session.cookies.set('timestamp', tValue, domain=self.server_url.replace("https://", ""))
        self.session.headers.update({
            "X-Newrelic-Timestamp": tValue,
            "X-Requested-ID": requestedId,
            "X-Requested-With": "XMLHttpRequest",
        })

        response = self.session.post(answer_url, data=answer_data)
        print(response.text)
